/*
 * material.h
 *
 *  Created on: 2012-12-12
 *      Author: shane
 */

#ifndef MATERIAL_H_
#define MATERIAL_H_
#include "EME_exception.h"
#include "EME_definition.h"
#include<complex>
#include<vector>
#include<utility>
#include<cmath>
namespace EME
{

///Exceptions for material class
class EME_material_logic_error: public EME_logic_error
{
public:
	inline explicit EME_material_logic_error(const std::string&s,
			unsigned int code) :
			EME_logic_error(s, code)
	{
	}
	;
};

///The abstract class for lossless property
class lossless_dielectric
{
protected:
	double epsilon; ///< The dielectric constant, real number
	std::vector<std::pair<double, double> > sellmeier; ///< For described by Sellmeier Equation, using multiple pair of coefficents of B and C
public:
	inline lossless_dielectric() :
			epsilon(1.), sellmeier(std::vector<std::pair<double, double> >())
	{
	}
	;
	inline lossless_dielectric(const double& epsilon_in)
	{
#ifdef ERROR_CHECKING
		if (epsilon_in < EPS_MIN || epsilon_in > EPS_MAX)
			throw(EME_material_logic_error(
					"Dielectric coefficient configuration abnormal", 2001));
#endif
		sellmeier = std::vector<std::pair<double, double> >();
		epsilon = epsilon_in;
	}
	;
	inline lossless_dielectric(const lossless_dielectric& b)
	{
		if (b.sellmeier.size() != 0)
		{
			sellmeier = b.sellmeier;
			epsilon = 1.;
		}
		else
		{
#ifdef ERROR_CHECKING
			if (b.epsilon < EPS_MIN || b.epsilon > EPS_MAX)
				throw(EME_material_logic_error(
						"Dielectric coefficient copied abnormal", 2002));
#endif
			epsilon = b.epsilon;
			sellmeier = std::vector<std::pair<double, double> >();
		}
	}
	;
	inline lossless_dielectric(
			const std::vector<std::pair<double, double> >& sellmeier_in)
	{
#ifdef ERROR_CHECKING
		if (sellmeier_in.size() == 0)
			throw(EME_material_logic_error(
					"Sellmeier Equation not inputting anything", 2003));
#endif
		sellmeier = sellmeier_in;
		epsilon = 1.;
	}
	;
	inline ~lossless_dielectric()
	{
	}
	;
};
///structure for lorentz element
struct lorentz_element
{
	double sigma;
	double omega;
	double gamma;
};

///The abstract class for lossy property
class lossy_dielectric
{
protected:
	std::complex<double> epsilon; ///< The dielectric constant, complex number
	double sigma_D; ///< The electric conductivity
	double epsilon_infty; ///<  The instantaneous dielectric function
	std::vector<lorentz_element> lorentz; ///< Containers to contain coeffiencts for Drude-Lorentz Model
public:
	inline lossy_dielectric() :
			epsilon(1.), sigma_D(0.), epsilon_infty(1.), lorentz(
					std::vector<lorentz_element>())
	{
	}
	;
	inline lossy_dielectric(const std::complex<double>& epsilon_in)
	{
#ifdef ERROR_CHECKING
		if (epsilon_in.real() < EPS_MIN || epsilon_in.real() > EPS_MAX)
			throw(EME_material_logic_error(
					"Dielectric coefficient configuration abnormal(real part)",
					2005));
		if (epsilon_in.imag() < 0)
			throw(EME_material_logic_error(
					"Dielectric coefficient configuration abnormal(imag part)",
					2006));
#endif
		epsilon = epsilon_in;
		sigma_D = 0.;
		epsilon_infty = 1.;
		lorentz = std::vector<lorentz_element>();
	}
	;
	inline lossy_dielectric(const lossy_dielectric& b)
	{
		if (b.lorentz.size() != 0)
		{
			epsilon = 1.;
			sigma_D = b.sigma_D;
			epsilon_infty = b.epsilon_infty;
			lorentz = b.lorentz;
		}
		else
		{
#ifdef ERROR_CHECKING
			if (b.epsilon.real() < EPS_MIN || b.epsilon.real() > EPS_MAX)
				throw(EME_material_logic_error(
						"Dielectric coefficient copied abnormal(real part)",
						2007));
			if (b.epsilon.imag() < 0)
				throw(EME_material_logic_error(
						"Dielectric coefficient copied abnormal(imag part)",
						2008));
#endif
			epsilon = b.epsilon;
			sigma_D = 0.;
			epsilon_infty = 1.;
			lorentz = std::vector<lorentz_element>();
		}
	}
	;
	inline lossy_dielectric(const double& sigma_D_in,
			const double& epsilon_infty_in,
			const std::vector<lorentz_element>& lorentz_in)
	{
#ifdef ERROR_CHECKING
		if (lorentz_in.size()
				== 0||sigma_D_in<0||epsilon_infty_in<EPS_MIN||epsilon_infty_in>EPS_MAX)throw
(			EME_material_logic_error("Sellmeier Equation set abnormal", 2009));
#endif
			epsilon=0.;
			sigma_D=sigma_D_in;
			epsilon_infty=epsilon_infty_in;
			lorentz=lorentz_in;
		};
		inline ~lossy_dielectric()
		{};
	};

///The abstract class for material
class material
{
public:
	inline material()
	{
	}
	;
	inline material(const material& b)
	{
	}
	;
	inline virtual ~material()
	{
	}
	;
	virtual bool if_lossy()=0;
	virtual double get_epsilon_r(double lambda)=0;
	virtual std::complex<double> get_epsilon_c(double lambda)=0;
};

class lossless_material: public material, protected lossless_dielectric
{
public:
	inline lossless_material() :
			material(), lossless_dielectric()
	{
	}
	;
	inline lossless_material(const double& epsilon_in) :
			material(), lossless_dielectric(epsilon_in)
	{
	}
	;
	inline lossless_material(
			const std::vector<std::pair<double, double> >& sellmeier_in) :
			material(), lossless_dielectric(sellmeier_in)
	{
	}
	;
	inline virtual bool if_lossy()
	{
		return false;
	}
	;
	inline virtual double get_epsilon_r(const double& lambda)
	{
		if (sellmeier.size() == 0)
			return epsilon;
		double sum = 1.;
		for (std::vector<std::pair<double, double> >::iterator it =
				sellmeier.begin(); it != sellmeier.end(); ++it)
		{
			sum += (*it).first * lambda * lambda
					/ (lambda * lambda - (*it).second);
		}
#ifdef ERROR_CHECKING
		if (sum < EPS_MIN || sum > EPS_MAX)
			throw(EME_material_logic_error(
					"Dielectric coefficient set by sellmeier equation abnormal",
					2004));
#endif
		return sum;
	}
	inline virtual std::complex<double> get_epsilon_c(const double& lambda)
	{
		return get_epsilon_r(lambda);
	}
	;
	inline virtual ~lossless_material()
	{
	}
	;
};
class lossy_material: public material, protected lossy_dielectric
{
public:
	inline lossy_material() :
			material(), lossy_dielectric()
	{
	}
	;
	inline lossy_material(const std::complex<double>& epsilon_in) :
			material(), lossy_dielectric(epsilon_in)
	{
	}
	;
	inline lossy_material(const double& sigma_D_in,
			const double& epsilon_infty_in,
			const std::vector<lorentz_element>& lorentz_in) :
			material(), lossy_dielectric(sigma_D_in, epsilon_infty_in,
					lorentz_in)
	{
	}
	;
	inline virtual bool if_lossy()
	{
		return true;
	}
	;
	inline virtual double get_epsilon_r(const double& lambda)
	{
		throw(EME_material_logic_error("Material downgrade", 2010));
	}
	;
	inline virtual std::complex<double> get_epsilon_c(const double& lambda)
	{
		if (lorentz.size() == 0)
			return epsilon;
		double omega = 2 * M_PI * C_LIGHT / lambda;
		std::complex<double> result = std::complex<double>(1., sigma_D / omega);
		std::complex<double> sum = epsilon_infty;
		for (std::vector<lorentz_element>::iterator it = lorentz.begin();
				it != lorentz.end(); ++it)
		{
			sum += (*it).sigma * (*it).omega * (*it).omega
					/ std::complex<double>(
							((*it).omega * (*it).omega - omega * omega),
							-omega * (*it).gamma);
		}
		result *= sum;
#ifdef ERROR_CHECKING
		if (result.real() < EPS_MIN || result.real() > EPS_MAX)
			throw(EME_material_logic_error(
					"Dielectric coefficient set by Lorentz model abnormal",
					2010));
#endif
		return result;
	}
	;
};
}

#endif /* MATERIAL_H_ */
